package ServerThreads;

import Communication.TaskForModule;
import Communication.TaskForServer;
import Const.ClientConst;
import Const.ServerConst;
import Const.TaskConst;
import LiftEngine.Lift;
import Server.ClientList;
import Server.ServerClientInstance;
import Server.TaskIDGetter;
import java.util.LinkedList;

/**
 *
 * Class description goes here.
 *
 * @author Kamil Gajowy
 */
public class LiftOrganiser extends MyThreadServer {

    private long tTravelStart   =   0;
    private long tTravelTime    =   0;
    private int nextFloor       =   0;
    private int floorStart      =   0;
    private int tID             =   0;

    private LinkedList<TaskForServer> tList;
    private LinkedList<TaskForModule> taskMList;
    
    /**
    * Default constructor.
     * @param name
     * @param cListReference
     * @param liftRef
     * @param tList
     * @param taskMList 
     */
    public LiftOrganiser(String name,
            ClientList cListReference,
            Lift liftRef,
            LinkedList<TaskForServer> tList,
            LinkedList<TaskForModule> taskMList){
        super(name, cListReference, liftRef);
        this.tList = tList;
        this.taskMList = taskMList;
    }


    @Override
    public void run(){
        getLiftReference().updateLiftTask();
        while(serverRunning){
            try {
                //do stuff
                getLiftReference().updateLiftTask();    //znajdz nastepne pietro
                startTraveling();                       //zacznij podroz
                checkTravel();                          //sprawdz czy dotarla

                LiftOrganiser.sleep(100);
            } catch (InterruptedException ex) {
                break;
            }
        }//end while
    }

    /**
     * Inicjalizuje podroz windy.
     */
    private void startTraveling() {
        Lift lRef = getLiftReference();
        if (! lRef.isTraveling() && !isAnyoneDeciding()){

            int curFloor = lRef.getCurrentFloor();
            nextFloor = getNextFloor(curFloor);
            if ( nextFloor == ServerConst.NoMoreTaskToDo ) {
                return;
            }
            floorStart = curFloor;
            lRef.setTraveling(true);
            tTravelStart = System.currentTimeMillis();
            System.out.println("Move from " + curFloor + " to floor: " + nextFloor);
            //TODO add lift open/close
            tTravelTime = Math.abs(curFloor-nextFloor)
                    *ServerConst.LiftTravelToNextFloorTime;
        }

    }//end find task

    /**
     * Sprawdza, czy winda dojechala
     */
    private void checkTravel() {
        Lift lRef = getLiftReference();

        if(lRef.isTraveling()){
            int floor;
            if ( lRef.getLiftDirection() == ServerConst.LiftDirectionUp ){
                floor = (int)((System.currentTimeMillis() - tTravelStart)/
                    ServerConst.LiftTravelToNextFloorTime)+floorStart;
                if ( floor != lRef.getCurrentFloor() ){
                    System.out.println("$ at [ " + floor + " ] now...");
                    lRef.setCurrentFloor(floor);
                }
            }else{
                floor = floorStart - (int)
                        ((System.currentTimeMillis() - tTravelStart)/
                        ServerConst.LiftTravelToNextFloorTime);
                if ( floor != lRef.getCurrentFloor() ){
                    System.out.println("$ at [ " + floor + " ] now...");
                    lRef.setCurrentFloor(floor);
                }
            }
        }//curr floor ?

        if (lRef.isTraveling() &&
                tTravelTime+tTravelStart <= System.currentTimeMillis()){

            System.out.println("Arrived at: " + nextFloor);
            lRef.setCurrentFloor(nextFloor);

            unloadPersons();
            loadPersons();

            lRef.updateLiftTask();
            lRef.setTraveling(false);

            //
            //?  na pewno tutaj ?
            System.out.println("\t@\tgetting enxt floor");

            nextFloor = getNextFloor(nextFloor);
            //????

            tTravelTime = 0;
            tTravelStart = 0;

        }
    }

    private void unloadPersons() {
        int i,j;
        boolean delete = false;
        //unload first from this floor
        for ( j = 0 ; j < this.tList.size() ; ++j ){
            if ( tList.get(j).getActionToTake() != TaskConst.ActionUnloadPerson ){
                continue;
            }
            for ( i = 0 ; i < this.getClientList().getTravelingQueue().getClientList().size() ; ++i ){
                ServerClientInstance cl = getClientList().getTravelingQueue().getClientList().get(i);


                if ( tList.get(j).getFloorToTravel() != nextFloor ){
                    continue;
                }

                if ( cl.getClient().getId() == tList.get(j).getClientID() &&
                        cl.getClient().getDesiredFloor()
                        == this.getLiftReference().getCurrentFloor()){
                    //unload
                    cl.getClient().setState(ClientConst.ClientExit);
                    getClientList().getConnectedQueue().addPerson(cl);
                    getClientList().getTravelingQueue().removePerson(cl);
                    synchronized(taskMList){
                        TaskIDGetter.getInstance().incrementID();
                        taskMList.add(new TaskForModule(TaskIDGetter.getInstance().getID(),cl.getClient(),TaskConst.ActionPersonLeftTheLift));
                    }
                    cl.getClient().setCurrentFloor(nextFloor);
                    int last = this.getLiftReference().getLastDoneID();
                    int clast = this.tList.get(j).getTaskID();
                    if (clast > last ) {
                        this.getLiftReference().setLastDoneID(clast);
                    }
                    delete = true;
                    System.out.println("PERSON UNLOADED");
                    break;//break for next task
                }
            }
            if ( delete ){
                System.out.println("task done!\tremoving...task ID was "+tList.get(j).getTaskID()+"\t size now "+tList.size());
                tList.remove(tList.get(j));
                System.out.println("task done!\tremoving...size now "+tList.size());
                delete = false;
                //--j;
            }
        }
    }//end unload person

    private void loadPersons() {
        System.out.println("louding persons tList size = " + tList.size());
        int i,j;
        boolean delete = false;
        //unload first from this floor
        for ( j = 0 ; j < this.tList.size() ; ++j ){
            if ( tList.get(j).getActionToTake() != TaskConst.ActionLoadPerson ){
                continue;
            }
            
            for ( i = 0 ; i < this.getClientList().getWaitingQueue().getClientList().size() ; ++i ){
                ServerClientInstance cl = getClientList().getWaitingQueue().getClientList().get(i);

                if ( tList.get(j).getFloorToTravel() != nextFloor ){
                    continue;
                }
                
                if ( cl.getClient().getId() == tList.get(j).getClientID()){
                    
                    
                    cl.getClient().setState(ClientConst.ClientDeciding);
                    getClientList().getTravelingQueue().addPerson(cl);
                    getClientList().getWaitingQueue().removePerson(cl);
                    //TODO WAITING
                    //synchronized(taskMList){
                    //    taskMList.add(new TaskForModule(tID++,cl.getClient(),TaskConst.ActionPersonJoinedTraveling));
                    //}
                    int last = this.getLiftReference().getLastDoneID();
                    int clast = this.tList.get(j).getTaskID();
                    if (clast > last ) {
                        this.getLiftReference().setLastDoneID(clast);
                    }
                    System.out.println("PERSON PICKED UP");
                    delete = true;
                    break;//break for next task
                }
            }
            if ( delete ){
                System.out.println("task done!\tremoving...task ID was "+tList.get(j).getTaskID()+"\t size now "+tList.size());
                tList.remove(tList.get(j));
                System.out.println("task done!\tremoving...size now "+tList.size());
                //--j;
                delete = false;
            }
        }
    }//end unload person

    private int getNextFloor(int currFloor) {
        //System.out.println("next floor .size now "+tList.size());
        if ( tList.isEmpty() ){
            System.out.println("no more task to do !\t\t###");
            return ServerConst.NoMoreTaskToDo;
        }
        else{
            //TODO remove task when finished ?
            if (tList.getFirst().getFloorToTravel() > currFloor ){
                this.getLiftReference().setLiftDirection(ServerConst.LiftDirectionUp);
            }
            if (tList.getFirst().getFloorToTravel() < currFloor ){
                this.getLiftReference().setLiftDirection(ServerConst.LiftDirectionDown);
            }
            return tList.getFirst().getFloorToTravel();
        }
    }


    private boolean isAnyoneDeciding(){
        int i;
        for ( i = 0 ; i < this.getClientList().getTravelingQueue().getClientList().size() ; ++i){
            if ( this.getClientList().getTravelingQueue().getClientList().get(i).getClient().getState()
                    == ClientConst.ClientDeciding) {
                return true;
            }
        }
        return false;
    }

}//end class
